﻿using System;
using System.Net;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Documents;
using System.Windows.Ink;
using System.Windows.Input;
using System.Windows.Media;
using System.Windows.Media.Animation;
using System.Windows.Shapes;
using System.Collections.Generic;
using System.Windows.Printing;
using System.Windows.Data;
using System.ComponentModel;

namespace ReflectionIT.Silverlight.ReportLibrary {

    public class ReportDocument {

        private Grid _visual;
        private StackPanel _panel;
        private ReportHeader _header;
        
        private int _currentParagraphIndex;
        private readonly List<FrameworkElement> _paragraphs = new List<FrameworkElement>();

        /// <summary>
        /// Occurs when a property is changed
        /// </summary>
        public event PropertyChangedEventHandler PropertyChanged;

        /// <summary>
        /// Occurs when the printing operation is complete or when the print operation
        ///  is cancelled by the application author.
        /// </summary>
        public event EventHandler<EndPrintEventArgs> EndPrint;

        /// <summary>
        /// Occurs before a page is printed. If you cancel this event the page is not printed
        /// </summary>
        public event EventHandler<CancelEventArgs> PagePrinting;

        /// <summary>
        /// Occurs after a page is printed.
        /// </summary>
        public event EventHandler PagePrinted;

        public ReportDocument(string headerTitle) {

            _visual = new Grid();
            _panel = new StackPanel() {
                Margin = new Thickness(40),
            };
            _visual.Children.Add(_panel);

            Header = new ReportHeader();
            Header.Title = headerTitle;
        }

        /// <summary>
        /// Gets or sets the Header of the report
        /// </summary>
        public ReportHeader Header {
            get { return _header; }
            set { _header = value; }
        }

        /// <summary>
        /// Gets or sets the outer Margin of the report
        /// </summary>
        public Thickness Margin {
            get { return _panel.Margin; }
            set { _panel.Margin = value; }
        }

        /// <summary>
        /// Gets or sets the data context for the ReportDocument when it participates in databinding
        /// </summary>
        public object DataContext {
            get { return _panel.DataContext; }
            set { _panel.DataContext = value; }
        }

        /// <summary>
        /// Gets the paragraphs of the report
        /// </summary>
        public List<FrameworkElement> Paragraphs {
            get { return _paragraphs; }
        }

        /// <summary>
        /// Print the report with the title of the header as document name 
        /// </summary>
        public void Print() {
            this.Print(this.Header.Title);
        }

        /// <summary>
        /// Print the report
        /// </summary>
        /// <param name="documentName">Name of the document to print</param>
        public void Print(string documentName) {
            var pd = new PrintDocument();
            pd.PrintPage += new EventHandler<PrintPageEventArgs>(pd_PrintPage);
            pd.EndPrint += new EventHandler<EndPrintEventArgs>(pd_EndPrint);

            _currentParagraphIndex = 0;

            pd.Print(documentName);
        }

        /// <summary>
        /// PrintDocument.EndPrint event handler
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void pd_EndPrint(object sender, EndPrintEventArgs e) {
            OnEndPrint(e);          
        }

        /// <summary>
        /// Print the next page
        /// </summary>
        /// <param name="sender"></param>
        /// <param name="e"></param>
        private void pd_PrintPage(object sender, PrintPageEventArgs e) {
            Header.PageValue = (sender as PrintDocument).PrintedPageCount + 1;

            CancelEventArgs cancel = new CancelEventArgs();
            OnPagePrinting(cancel);
            if (cancel.Cancel) {
                e.HasMorePages = false;
            } else {
                // Not canceled, print page

                if (_panel.Children.Count == 0) {
                    // First page!
                    _panel.Children.Add(Header);
                } else {
                    // remove all except header
                    while (_panel.Children.Count > 1) {
                        _panel.Children.RemoveAt(1);
                    }
                }
                _visual.Height = e.PrintableArea.Height;
                _visual.Width = e.PrintableArea.Width;

                double totalHeight = e.PrintableArea.Height - this.Margin.Top - this.Margin.Bottom;
                double totalWidth = e.PrintableArea.Width - this.Margin.Left - this.Margin.Right;

                double height;
                if (_header.ActualHeight > 0) {
                    height = Header.ActualHeight;
                } else {
                    Header.Measure(new Size(totalWidth, totalHeight));
                    height = Header.DesiredSize.Height;
                }

                while (_currentParagraphIndex < Paragraphs.Count) {
                    var paragraph = Paragraphs[_currentParagraphIndex];
                    if (paragraph is PageBreak) {
                        _currentParagraphIndex++;
                        break;
                    }
                    if (paragraph.ActualHeight > 0) {
                        height += paragraph.ActualHeight;
                    } else {
                        var size = new Size(totalWidth, totalHeight - height);
                        paragraph.Measure(size);
                        height += paragraph.DesiredSize.Height;
                    }
                    if (height > totalHeight) {
                        // doesn't fit anymore
                        break;
                    }
                    _currentParagraphIndex++;
                    _panel.Children.Add(paragraph);
                }

                e.PageVisual = _visual;
                e.HasMorePages = _currentParagraphIndex < Paragraphs.Count;

                OnPagePrinted();
            }
        }

        protected virtual void OnEndPrint(EndPrintEventArgs e) {
            var handler = EndPrint;
            if (handler != null) {
                handler(this, e);
            }
        }

        protected virtual void OnPagePrinted() {
            EventHandler handler = PagePrinted;
            if (handler != null) {
                handler(this, EventArgs.Empty);
            }
        }

        protected virtual void OnPagePrinting(CancelEventArgs e) {
            var handler = PagePrinting;
            if (handler != null) {
                handler(this, e);
            }
        }


    }
}
